package com.cathyjava.easyflows.workflow;

import com.cathyjava.easyflows.work.Work;
import com.cathyjava.easyflows.work.WorkContext;
import com.cathyjava.easyflows.work.WorkPredicate;
import com.cathyjava.easyflows.work.WorkReport;
import com.cathyjava.easyflows.work.impl.NoOpWork;

import java.util.UUID;

/**
 * @author chenjing
 * @date 2021/5/8
 * @description 关注一下times实现方式
 */
public class RepeatFlow extends AbstractWorkFlow {
    private String name;
    private Work work;
    private WorkPredicate workPredicate;

    private RepeatFlow(String name, Work work, WorkPredicate workPredicate) {
        super(name);
        this.work = work;
        this.workPredicate = workPredicate;
    }

    @Override
    public WorkReport execute(WorkContext workContext) {
        WorkReport workReport = null;
        do {
            workReport = this.work.execute(workContext);
        } while (this.workPredicate.apply(workReport));
        return workReport;
    }

    public static class Builder {
        private Builder() {

        }

        public static NamedStep aNewRepeatFlow() {
            return new BuildSteps();
        }

        public interface NamedStep extends RepeatStep {
            RepeatStep named(String name);
        }

        public interface RepeatStep {
            UntilStep repeat(Work work);
        }

        public interface UntilStep {
            BuildStep until(WorkPredicate predicate);

            BuildStep times(Integer n);
        }

        public interface BuildStep {
            RepeatFlow build();
        }

        private static class BuildSteps implements NamedStep, RepeatStep, BuildStep, UntilStep {
            private String name;
            private Work work;
            private WorkPredicate predicate;

            public BuildSteps() {
                this.name = UUID.randomUUID().toString();
                this.work = new NoOpWork();
                this.predicate = WorkPredicate.ALWAYS_FALSE;
            }

            @Override
            public RepeatStep named(String name) {
                this.name = name;
                return this;
            }

            @Override
            public UntilStep repeat(Work work) {
                this.work = work;
                return this;
            }

            @Override
            public BuildStep until(WorkPredicate predicate) {
                this.predicate = predicate;
                return this;
            }

            @Override
            public BuildStep times(Integer n) {
                return until(WorkPredicate.TimesWorkPredicate.times(n));
            }

            @Override
            public RepeatFlow build() {
                return new RepeatFlow(this.name, this.work, this.predicate);
            }

        }
    }
}
